﻿using Microsoft.VisualBasic.CompilerServices;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Tnelab.Miniblink;

namespace Tnelab.MiniblinkWpf
{
    /// <summary>
    /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
    ///
    /// Step 1a) Using this custom control in a XAML file that exists in the current project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is 
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:Tnelab.MBWpf"
    ///
    ///
    /// Step 1b) Using this custom control in a XAML file that exists in a different project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is 
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:Tnelab.MBWpf;assembly=Tnelab.MBWpf"
    ///
    /// You will also need to add a project reference from the project where the XAML file lives
    /// to this project and Rebuild to avoid compilation errors:
    ///
    ///     Right click on the target project in the Solution Explorer and
    ///     "Add Reference"->"Projects"->[Select this project]
    ///
    ///
    /// Step 2)
    /// Go ahead and use your control in the XAML file.
    ///
    ///     <MyNamespace:CustomControl1/>
    ///
    /// </summary>
    public abstract class MBWebViewBase : Control
    {
        private string DebugConfigCmd { get; set; }
        private string DebugConfigParam { get; set; }
        protected IWebView WebView { get; private set; }
        protected WriteableBitmap WriteBitMap { get; set; }

        static MBWebViewBase()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MBWebViewBase), new FrameworkPropertyMetadata(typeof(MBWebViewBase)));

        }
        
        public MBWebViewBase()
        {
            this.Loaded += MBWebViewBase_Loaded;
            this.Unloaded += MBWebViewBase_Unloaded;
        }
        public void SetDebugConfig(string cmd, string param)
        {
            this.DebugConfigCmd = cmd;
            this.DebugConfigParam = param;
        }
        private void MBWebViewBase_Unloaded(object sender, RoutedEventArgs e)
        {
            if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
            {
                this.WebView.Dispose();
            }
        }

        private void MBWebViewBase_Loaded(object sender, RoutedEventArgs e)
        {
            if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
            {
                this.WebView = OnCreateWebView();
                this.WebView.SetDebugConfig(DebugConfigCmd, DebugConfigParam);
                Window window = Window.GetWindow(this);
                Point point = this.TransformToAncestor(window).Transform(new Point(0, 0));
                this.WebView.SetPosition((int)point.X, (int)point.Y);
                WebView.PaintBitUpdated += WebView_PaintBitUpdated;
            }
        }

        protected abstract IWebView OnCreateWebView();
        object _lockWriteBitMap = new object();
        private void WebView_PaintBitUpdated(object sender, PaintBitUpdatedEventArgs e)
        {
            if (this.ActualWidth > 0 && this.ActualHeight > 0)
            {
                Tnelab.Miniblink.Size size;
                if (!this.WebView.IsVip)
                {
                    size = this.WebView.Size;
                }
                else
                {
                    size = new Miniblink.Size((int)this.ActualWidth, (int)this.ActualHeight);
                }
                if (WriteBitMap != null && WriteBitMap.Width == size.Width && WriteBitMap.Height == size.Height && WriteBitMap.Width == e.Width && WriteBitMap.Height == e.Height) 
                {
                    using var stream = e.Buffer;
                    using var memStream = new MemoryStream();
                    stream.CopyTo(memStream);
                    lock (_lockWriteBitMap)
                    {
                        WriteBitMap.Lock();
                        WriteBitMap.WritePixels(new System.Windows.Int32Rect(0, 0, WriteBitMap.PixelWidth, WriteBitMap.PixelHeight), memStream.ToArray(), size.Width * 4, 0, 0);
                        WriteBitMap.Unlock();
                    }
                }
                else
                {
                    try
                    {
                        BitmapPalette palette = new BitmapPalette(new List<System.Windows.Media.Color>() { Colors.Blue, Colors.Green, Colors.Red });
                        using var stream = e.Buffer;
                        using var memStream= new MemoryStream();
                        stream.CopyTo(memStream);
                        BitmapSource src = BitmapSource.Create(size.Width, size.Height, 96, 96, PixelFormats.Bgra32, palette, memStream.ToArray(), size.Width*4);
                        lock (_lockWriteBitMap)
                        {
                            WriteBitMap = new WriteableBitmap(src);
                        }
                    }
                    catch (System.AccessViolationException)
                    {

                    }
                }
                this.InvalidateVisual();
            }
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
            {
                var rect = new System.Windows.Rect();
                rect.Width = this.ActualWidth;
                rect.Height = this.ActualHeight;
                lock (_lockWriteBitMap)
                {
                    drawingContext.DrawImage(WriteBitMap, rect);
                }
            }
            else
            {
                var point = new System.Windows.Point();
                point.X = 0;
                point.Y = 0;
                var ft = new FormattedText(
                    "MBWebView", 
                    CultureInfo.CurrentUICulture,
                    FlowDirection.LeftToRight, 
                    new Typeface("Verdana"),
                    32,
                    Brushes.Red,
                    VisualTreeHelper.GetDpi(this).PixelsPerDip
                    );
                
                drawingContext.DrawText(ft, point);
            }
        }

        public static readonly DependencyProperty UrlProperty =
        DependencyProperty.Register("Url", typeof(Uri), typeof(MBWebViewBase), new PropertyMetadata(new Uri("https://wwww.baidu.com"),new PropertyChangedCallback(OnValueChanged)));
        public Uri Url
        {
            get { return (Uri)GetValue(UrlProperty); }
            set { 
                SetValue(UrlProperty, value);
            }
        }
        public static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var _this = (d as MBWebViewBase);
            if (e.Property.Name == "Url")
            {
                if (_this.WebView != null)
                {
                    _this.WebView.LoadUrl(_this.Url.ToString());
                }

            }
        }
        bool IsExtendedKey(Key key)
        {
            switch (key)
            {
                case Key.Insert:
                case Key.Delete:
                case Key.Home:
                case Key.End:
                case Key.Prior:
                case Key.Next:
                case Key.Left:
                case Key.Right:
                case Key.Up:
                case Key.Down:
                    return true;
                default:
                    return false;
            }
        }
        #region 消息处理
        protected override void OnRenderSizeChanged(System.Windows.SizeChangedInfo sizeInfo)
        {
            if (this.WebView != null)
            {
                Window window = Window.GetWindow(this);
                Point point = this.TransformToAncestor(window).Transform(new Point(0, 0));
                this.WebView.SetPosition((int)point.X, (int)point.Y);
                this.WebView.Resize((int)this.ActualWidth, (int)this.ActualHeight);
            }
        }
        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            var key = e.SystemKey == Key.None ? e.Key : e.SystemKey;
            int code = KeyInterop.VirtualKeyFromKey(key);
            var flags = (uint)KeyFlags.REPEAT;

            if (IsExtendedKey(key))
            {
                flags |= (uint)KeyFlags.EXTENDED;
            }

            if (WebView.FireKeyDownEvent((uint)code, flags, false))
            {
                e.Handled = false;
            }

            base.OnPreviewKeyDown(e);
        }

        protected override void OnPreviewKeyUp(KeyEventArgs e)
        {
            var key = e.SystemKey == Key.None ? e.Key : e.SystemKey;
            int code = KeyInterop.VirtualKeyFromKey(key);
            var flags = (uint)KeyFlags.REPEAT;

            if (IsExtendedKey(key))
            {
                flags |= (uint)KeyFlags.EXTENDED;
            }

            if (WebView.FireKeyUpEvent((uint)code, 0, false))
            {
                e.Handled = false;
            }

            base.OnPreviewKeyUp(e);
        }


        protected override void OnGotFocus(System.Windows.RoutedEventArgs e)
        {
            base.OnGotFocus(e);

            if (WebView != null)
            {
                WebView.SetFocus();
            }
        }

        protected override void OnLostFocus(System.Windows.RoutedEventArgs e)
        {
            base.OnLostFocus(e);

            if (WebView != null)
            {
                WebView.KillFocus();
            }
        }

        private static uint GetMouseFlags(MouseEventArgs e)
        {
            uint flags = 0;


            if (e.LeftButton == MouseButtonState.Pressed)
            {
                flags = flags | (uint)MouseFlags.LBUTTON;
            }
            else if (e.MiddleButton == MouseButtonState.Pressed)
            {
                flags = flags | (uint)MouseFlags.MBUTTON;
            }
            else if (e.RightButton == MouseButtonState.Pressed)
            {
                flags = flags | (uint)MouseFlags.RBUTTON;
            }



            //判断键盘按键
            if (Keyboard.Modifiers == ModifierKeys.Control)
            {
                flags = flags | (uint)MouseFlags.CONTROL;
            }
            if (Keyboard.Modifiers == ModifierKeys.Shift)
            {
                flags = flags | (uint)MouseFlags.SHIFT;
            }
            return flags;
        }

        /// <summary>
        /// 文本输入
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreviewTextInput(TextCompositionEventArgs e)
        {
            base.OnPreviewTextInput(e);
            if (WebView != null)
            {
                for (int i = 0; i < e.Text.Length; i++)
                {
                    WebView.FireKeyPressEvent( e.Text[i], 0, false);
                }
            }
        }

        public new void TextInput(string text)
        {
            if (WebView != null)
            {
                for (int i = 0; i < text.Length; i++)
                {
                    WebView.FireKeyPressEvent( text[i], 0, false);

                }
            }
        }


        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);
            var point = e.GetPosition(this);
            if (WebView != null)
            {
                uint flags = (uint)GetMouseFlags(e);
                WebView.FireMouseWheelEvent( (int)point.X, (int)point.Y, e.Delta, flags);
            }

        }

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);

            if (WebView != null)
            {
                Focus();
                uint msg = 0;
                if (e.ChangedButton == MouseButton.Left)
                {
                    msg = (uint)MouseMsg.LBUTTONDOWN;
                }
                else if (e.ChangedButton == MouseButton.Middle)
                {
                    msg = (uint)MouseMsg.MBUTTONDOWN;
                }
                else if (e.ChangedButton == MouseButton.Right)
                {
                    msg = (uint)MouseMsg.RBUTTONDOWN;
                }
                var point = e.GetPosition(this);
                uint flags = GetMouseFlags(e);
                WebView.FireMouseEvent( msg, (int)point.X, (int)point.Y, flags);
            }
        }

        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);
            if (WebView != null)
            {
                uint msg = 0;
                if (e.ChangedButton == MouseButton.Left)
                {
                    msg = (uint)MouseMsg.LBUTTONUP;
                }
                else if (e.ChangedButton == MouseButton.Middle)
                {
                    msg = (uint)MouseMsg.MBUTTONUP;
                }
                else if (e.ChangedButton == MouseButton.Right)
                {
                    msg = (uint)MouseMsg.RBUTTONUP;
                }

                var point = e.GetPosition(this);

                uint flags = GetMouseFlags(e);

                WebView.FireMouseEvent( msg, (int)point.X, (int)point.Y, flags);
            }
        }



        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (WebView != null)
            {
                uint flags = GetMouseFlags(e);
                var point = e.GetPosition(this);                
                WebView.FireMouseEvent((uint)MouseMsg.MOUSEMOVE, (int)point.X, (int)point.Y, flags);
                if (!this.WebView.IsVip)
                {
                    SetCursors();
                }
            }
        }


        void SetCursors()
        {
            switch (WebView.GetCursorType())
            {
                case Miniblink.CursorType.Pointer:
                    Cursor = null;
                    break;
                case Miniblink.CursorType.Cross:
                    Cursor = Cursors.Cross;
                    break;
                case Miniblink.CursorType.Hand:
                    Cursor = Cursors.Hand;
                    break;
                case Miniblink.CursorType.IBeam:
                    Cursor = Cursors.IBeam;
                    break;
                case Miniblink.CursorType.Wait:
                    Cursor = Cursors.Wait;
                    break;
                case Miniblink.CursorType.Help:
                    Cursor = Cursors.Help;
                    break;
                case Miniblink.CursorType.EastResize:
                    Cursor = Cursors.SizeWE;
                    break;
                case Miniblink.CursorType.NorthResize:
                    Cursor = Cursors.SizeNS;
                    break;
                case Miniblink.CursorType.NorthEastResize:
                    Cursor = Cursors.SizeNESW;
                    break;
                case Miniblink.CursorType.NorthWestResize:
                    Cursor = Cursors.SizeNWSE;
                    break;
                case Miniblink.CursorType.SouthResize:
                    Cursor = Cursors.SizeWE;
                    break;
                case Miniblink.CursorType.SouthEastResize:
                    Cursor = Cursors.SizeNWSE;
                    break;
                case Miniblink.CursorType.SouthWestResize:
                    Cursor = Cursors.SizeNESW;
                    break;
                case Miniblink.CursorType.WestResize:
                    Cursor = Cursors.SizeWE;
                    break;
                case Miniblink.CursorType.NorthSouthResize:
                    Cursor = Cursors.SizeNS;
                    break;
                case Miniblink.CursorType.EastWestResize:
                    Cursor = Cursors.SizeWE;
                    break;
                case Miniblink.CursorType.NorthEastSouthWestResize:
                    Cursor = Cursors.SizeAll;
                    break;
                case Miniblink.CursorType.NorthWestSouthEastResize:
                    Cursor = Cursors.SizeAll;
                    break;
                case Miniblink.CursorType.ColumnResize:
                    Cursor = null;
                    break;
                case Miniblink.CursorType.RowResize:
                    Cursor = null;
                    break;
                default:
                    Cursor = null;
                    break;
            }
        }
        #endregion
    }
}
